<?php

namespace App\Http\Controllers;

//分布式锁演示controller
use App\Http\Service\RedisService;
use App\Models\User;
use App\Utils\MyRedis;
use Illuminate\Http\Request;

class Redis extends Controller
{
    // 页面连接websocket服务器

    // 演示获取方法，防止缓存穿透
    public function getFirstUserInfoByName(Request $request): \Illuminate\Http\JsonResponse
    {
        $name = $request->input('name');
        if (!$name) {
            return response()->json(create_response_data('姓名不能为空！', false));
        }
        //获取用户名 数据库中有
        //管理员 张三 李四 王五 赵六 钱七 路八 王九
        //redis:user:name:'张三‘ = {}
        $redis_key = "redis:user:name:$name";
        try {
            $myRedis = MyRedis::getInstance();
            $callback = function ($name) {
                // 从数据库获取第一个userInfo的方法
                return User::getFirstUserInfoByName($name);
            };
            // 这里如果redis和数据库中都没有获取到则会设定一个30秒的空值 防止缓存击穿
            $user_info = $myRedis->getHashAndSource($redis_key, $callback, [$name]);
            $user_info['time'] = date('H:i:s', time());
            $user_info['name'] = $name;
            return response()->json(create_response_data('成功', true, $user_info));
        } catch (\RedisException $e) {
            return response()->json(create_response_data('获取数据失败', false));
        }
    }

    // 点击创建分布式锁
    // 业务显示锁倒计时
    // 当watchdog触发的时候给页面一个提示
    // 分布式锁演示
    public function distributedLock(Request $request)
    {
        $uid = $request->input('token_uid');
        //分布式锁演示
        $lock_name = 'demo:' . $uid;
        $myRedis = MyRedis::getInstance();
        //假如程序需要执行5秒 并且这里是未使用消息队列的会同步去创建
        $call_back = function () {
            sleep(15);
        };
        try {
            //定时任务会每10秒执行一次watchdog重置分布式锁 系统宕机之后锁过期后就会自动消失
            $res = $myRedis->distributedLock($lock_name, $call_back, [], 15);
            $date = date('H:i:s', time());
            if (!$res) {
                return response()->json(create_response_data('已锁定无法创建', false, ['time' => $date]));
            } else {
                return response()->json(create_response_data('成功', true, ['time' => $date]));
            }
        } catch (\Exception $e) {
            $date = date('H:i:s', time());
            return response()->json(create_response_data('业务代码执行出错', false, ['time' => $date]));
        }
    }

    // 分布式锁+消息队列(rabbitMQ)
    // 首先分布式锁的删除的方法需要通过方法调用 并且可以设置是否自行删除
    // 消息队列中需要带上分布式锁的uuid用来识别是否消费 并且用于删除锁
    // 然后使用两种队列的方式 一种是延迟的队列 一种是非延迟的队列
    public function distributedLockAndRabbitMQ(Request $request)
    {
        $uid = $request->input('token_uid');
        // 回调函数中写一个生产消息的方法
        $myRedis = MyRedis::getInstance();
        $lock_name = 'rabbitmq:' . $uid;
        $call_back = function ($uuid, $uid, $lock_name) {
            // 回调函数之中写入MQ
            $redisService = new RedisService();
            $redisService->insertDemoMQ($uuid, $uid, $lock_name);
            // MQ消费者消费之后删除锁
            // 删除锁的时候创建一个延迟的消息用来模拟一个订单取消的过程
        };
        $args = [$uid, $lock_name];
        // 创建分布式锁 并且设置不自动删除 这里分布式锁
        $res = $myRedis->distributedLock($lock_name, $call_back, $args, 20, false);
        $date = date('H:i:s', time());
        if(!$res) {
            return response()->json(create_response_data('已锁定无法创建', false, ['time' => $date]));
        } else {
            return response()->json(create_response_data('成功', true, ['time' => $date]));
        }
    }

    // 哪里需要发送websocket消息呢
    // 消息队列消费之后/watchdog重制了锁之后
    // 这样消费队列的时候怎么才能知道呢
    // 只能存到消息队列之中才能知道 watchdog里面通过特定锁的uid来获取之后再发送

}
